//
//  ViewController.swift
//  hh
//
//  Created by SiriCao on 2017/4/28.
//  Copyright © 2017年 SiriCao. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    let vc = ViewController2()
    let vc2 = ViewController3()
    lazy var transitionControllerMgr: XOTransitionMgr = XOTransitionMgr()
    
    var nav: UINavigationController?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .red
        // Do any additional setup after loading the view, typically from a nib.
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
        //        panGesture.delegate = self
        view.addGestureRecognizer(panGesture)
        
        self.nav?.delegate = transitionControllerMgr
        let transitionController = SwipTransition.init(parentVC: vc, shouldAddGesture: false)
        vc.transitionController = transitionController
        
        let transitionController1 = SwipTransition.init(parentVC: vc2, shouldAddGesture: false)
        transitionController1.effctiveDirection = .down
        transitionController1.swipAction = .push
        vc2.transitionController = transitionController1
    }
    
    var beganSwipDirection: SwipDirection?
    func handlePan(_ gesture: UIPanGestureRecognizer) {
        let direction = XOUtils.swipDirection(with: gesture)
        if case .began = gesture.state {
            beganSwipDirection = direction
        }
        
        if let transitionController = vc.transitionController as? SwipTransition, beganSwipDirection == .left {
            transitionController.transit(with: gesture)
        } else if let transitionController = vc2.transitionController as? SwipTransition, beganSwipDirection == .down {
            transitionController.transit(with: gesture)
        }
    }
    
}

class ViewController2: UIViewController, XONavigationControllerProtocol {
    var pushAnimation: UIViewControllerAnimatedTransitioning? = CameraTransitionAnimation()
    var popAnimation: UIViewControllerAnimatedTransitioning? = CameraTransitionAnimation()
    var pushAnimationWithoutGesture: UIViewControllerAnimatedTransitioning?
    var popAnimationWithoutGesture: UIViewControllerAnimatedTransitioning?
    var transitionController : UIViewControllerInteractiveTransitioning?
    var transitWithGesture = true
    var originFrame: CGRect?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .white
        // Do any additional setup after loading the view, typically from a nib.
        
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(onPanGesture(_:)))
        panGesture.cancelsTouchesInView = false
        view.addGestureRecognizer(panGesture)
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        if let transitionController = transitionController as? SwipTransition {
            transitionController.effctiveDirection = .right
            transitionController.swipAction = .pop
        }
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        if let transitionController = transitionController as? SwipTransition {
            transitionController.effctiveDirection = .left
            transitionController.swipAction = .push
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    var beganSwipDirection: SwipDirection?
    func onPanGesture(_ gesture: UIPanGestureRecognizer) {
        let direction = XOUtils.swipDirection(with: gesture)
        if case .began = gesture.state {
            beganSwipDirection = direction
        }
        if let transitionController = transitionController as? SwipTransition, beganSwipDirection == .right {
            transitionController.transit(with: gesture)
        }
    }
}

class ViewController3: UIViewController, XONavigationControllerProtocol {
    var pushAnimation: UIViewControllerAnimatedTransitioning? = CameraTransitionAnimation1()
    var popAnimation: UIViewControllerAnimatedTransitioning? = CameraTransitionAnimation1()
    var pushAnimationWithoutGesture: UIViewControllerAnimatedTransitioning?
    var popAnimationWithoutGesture: UIViewControllerAnimatedTransitioning?
    var transitionController : UIViewControllerInteractiveTransitioning?
    var transitWithGesture = true
    var originFrame: CGRect?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .blue
        // Do any additional setup after loading the view, typically from a nib.
        
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(onPanGesture(_:)))
        panGesture.cancelsTouchesInView = false
        view.addGestureRecognizer(panGesture)
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        if let transitionController = transitionController as? SwipTransition {
            transitionController.effctiveDirection = .up
            transitionController.swipAction = .pop
        }
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        if let transitionController = transitionController as? SwipTransition {
            transitionController.effctiveDirection = .down
            transitionController.swipAction = .push
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    var beganSwipDirection: SwipDirection?
    func onPanGesture(_ gesture: UIPanGestureRecognizer) {
        let direction = XOUtils.swipDirection(with: gesture)
        if case .began = gesture.state {
            beganSwipDirection = direction
        }
        if let transitionController = transitionController as? SwipTransition, beganSwipDirection == .up {
            transitionController.transit(with: gesture)
        }
    }
}


public enum  SwipDirection {
    case unkown         //系统bug会导致短时间内的第二次滑动x、y都为0的情况
    case other           //45度角方向
    case up
    case down
    case left
    case right
}

class XOUtils {
    public class func swipDirection(with gesture: UIPanGestureRecognizer) -> SwipDirection {
        let lrRadio: CGFloat = 2.0
        let udRadio: CGFloat = 0.8
        
        let v = gesture.velocity(in: gesture.view)
        if v.x == 0 && v.y == 0 {
            return .unkown
        }
        if abs(v.x) > lrRadio * fabs(v.y) {
            return v.x > 0 ? .right : .left
        } else if abs(v.y) > udRadio * fabs(v.x) {
            return v.y > 0 ? .down : .up
        } else {
            return .other
        }
    }
}

class SwipTransition: UIPercentDrivenInteractiveTransition {
    weak var parentVC: UIViewController?
    var effctiveDirection: SwipDirection = .left
    var swipAction: UINavigationControllerOperation = .push
    
    let window = UIApplication.shared.delegate?.window
    
    init(parentVC: UIViewController, shouldAddGesture: Bool = true) {
        self.parentVC = parentVC
        super.init()
    }
    
    func transit(with gesture:UIPanGestureRecognizer) {
        guard let window = window else {
            return
        }
        
        let translation = gesture.translation(in: window)
        
        switch gesture.state {
        case .began:
            if let rvc = window?.rootViewController as? UINavigationController, swipAction == .push {
                rvc.pushViewController(parentVC!, animated: true)
            } else if let rvc = window?.rootViewController as? UINavigationController, swipAction == .pop {
                rvc.popViewController(animated: true)
            }
        case .changed:
            if effctiveDirection == .down || effctiveDirection == .up {
                let offset = fabs(translation.y)
                update(offset/UIScreen.main.bounds.height)
            } else {
                let offset = fabs(translation.x)
                update(offset/UIScreen.main.bounds.width)
            }
        case .ended, .cancelled:
            if ((effctiveDirection == .left || effctiveDirection == .right) && fabs(translation.x) > UIScreen.main.bounds.width*0.5) ||
                ((effctiveDirection == .down || effctiveDirection == .up) && fabs(translation.y) > UIScreen.main.bounds.height*0.33) {
                finish()
            } else {
                cancel()
            }
        default: break
        }
    }
}

class CameraTransitionAnimation : NSObject, UIViewControllerAnimatedTransitioning {
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.3
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) ,
            let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else {
                return
        }
        let containerView = transitionContext.containerView
        
        if toViewController is ViewController2 {
            //CameraViewController Push
            containerView.insertSubview(toViewController.view, belowSubview:fromViewController.view)
            let trans = CGAffineTransform(translationX: 0, y: 0)
            fromViewController.view.transform = trans
            
        } else {
            containerView.insertSubview(toViewController.view, aboveSubview:fromViewController.view)
            
            let trans = CGAffineTransform(translationX: -UIScreen.main.bounds.width, y: 0)
            toViewController.view.transform = trans
        }
        
        let duration = transitionDuration(using: transitionContext)
        UIView.animate(withDuration: duration, animations: {
            if toViewController is ViewController2 {
                let trans = CGAffineTransform(translationX: -UIScreen.main.bounds.width, y: 0)
                fromViewController.view.transform = trans
            } else {
                let trans = CGAffineTransform(translationX: 0, y: 0)
                toViewController.view.transform = trans
            }
        }) { finished in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }
    }
}

class CameraTransitionAnimation1 : NSObject, UIViewControllerAnimatedTransitioning {
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.3
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) ,
            let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else {
                return
        }
        let containerView = transitionContext.containerView
        
        if toViewController is ViewController3 {
            //CameraViewController Push
            containerView.insertSubview(toViewController.view, aboveSubview:fromViewController.view)
            let trans = CGAffineTransform(translationX: 0, y: -UIScreen.main.bounds.height)
            toViewController.view.transform = trans
            
        } else {
            containerView.insertSubview(toViewController.view, belowSubview:fromViewController.view)
            
            let trans = CGAffineTransform(translationX: 0, y: 0)
            fromViewController.view.transform = trans
        }
        
        let duration = transitionDuration(using: transitionContext)
        UIView.animate(withDuration: duration, animations: {
            if toViewController is ViewController3 {
                let trans = CGAffineTransform(translationX: 0, y: 0)
                toViewController.view.transform = trans
            } else {
                let trans = CGAffineTransform(translationX: 0, y: -UIScreen.main.bounds.height)
                fromViewController.view.transform = trans
            }
        }) { finished in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }
    }
}

protocol XONavigationControllerProtocol {
    var pushAnimation: UIViewControllerAnimatedTransitioning? {get set}
    var popAnimation: UIViewControllerAnimatedTransitioning? {get set}
    var pushAnimationWithoutGesture: UIViewControllerAnimatedTransitioning? {get set}       //为空时使用pushAnimation
    var popAnimationWithoutGesture: UIViewControllerAnimatedTransitioning? {get set}        //为空时使用popAnimation
    
    var transitWithGesture: Bool {get set}
    var transitionController: UIViewControllerInteractiveTransitioning? {get set}
    var originFrame: CGRect? {get set}
}

class XOTransitionMgr: NSObject, UINavigationControllerDelegate {
    
    weak var fromViewController: UIViewController?
    weak var toViewController: UIViewController?
    var navigationOperation: UINavigationControllerOperation?
    
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        navigationOperation = operation
        toViewController = toVC
        fromViewController = fromVC
        if let naviToVC = toVC as? XONavigationControllerProtocol, operation == .push {
            var animation = naviToVC.transitWithGesture ? naviToVC.pushAnimation : naviToVC.pushAnimationWithoutGesture
            if animation == nil {
                animation = naviToVC.pushAnimation
            }
            if let frame = naviToVC.originFrame, let animation = animation as? XOBaseTransitionAnimation {
                animation.originFrame = frame
                print("let frame = naviToVC.originFrame, let animation = animation as? XOBaseTransitionAnimation")
            }
            return animation
        } else if let naviFromVC = fromVC as? XONavigationControllerProtocol, operation == .pop {
            var animation = naviFromVC.transitWithGesture ? naviFromVC.popAnimation : naviFromVC.popAnimationWithoutGesture
            if animation == nil {
                animation = naviFromVC.popAnimation
            }
            if let frame = naviFromVC.originFrame, let animation = animation as? XOBaseTransitionAnimation {
                animation.originFrame = frame
                print("let frame = naviFromVC.originFrame, let animation = animation as? XOBaseTransitionAnimation")
            }
            return animation
        } else {
            return nil
        }
    }
    
    func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        var transitionController: UIViewControllerInteractiveTransitioning?
        if let naviToVC = toViewController as? XONavigationControllerProtocol, navigationOperation == .push, naviToVC.transitWithGesture {
            transitionController = naviToVC.transitionController
            if let frame = naviToVC.originFrame, let transitionController = transitionController as? XOBaseTransition {
                transitionController.originFrame = frame
                print("let frame = naviToVC.originFrame, let transitionController = transitionController as? XOBaseTransition")
            }
        } else if let naviFromVC = fromViewController as? XONavigationControllerProtocol, navigationOperation == .pop, naviFromVC.transitWithGesture {
            transitionController = naviFromVC.transitionController
            if let frame = naviFromVC.originFrame, let transitionController = transitionController as? XOBaseTransition {
                transitionController.originFrame = frame
                print("let frame = naviFromVC.originFrame, let transitionController = transitionController as? XOBaseTransition")
            }
        }
        return transitionController
    }
}

class XOBaseTransition: UIPercentDrivenInteractiveTransition {
    var originFrame: CGRect = CGRect.zero
}

open class XOConfig {
    
}

extension XOConfig {
    struct TransitionAnimation {
        static let pureDuration = 0.3
        static let durationWithMask = 0.22
    }
}

class XOBaseTransitionAnimation: NSObject, UIViewControllerAnimatedTransitioning {
    var originFrame : CGRect = CGRect(x: 200, y: 100, width: 50, height: 50)
    
    var backgroundColor: UIColor = .blue
    
    init(backgroundColor: UIColor = .blue) {
        super.init()
        self.backgroundColor = backgroundColor
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        if let fromView = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)?.view,
            let toView = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)?.view {
            
            let backgroundView = UIView(frame: toView.frame)
            backgroundView.backgroundColor = backgroundColor
            
            transitionContext.containerView.addSubview(toView)
            transitionContext.containerView.addSubview(backgroundView)
            transitionContext.containerView.bringSubview(toFront: fromView)
            
            UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: {
                backgroundView.alpha = 0.0
            }, completion:{ finished in
                backgroundView.removeFromSuperview()
                if transitionContext.transitionWasCancelled {
                    toView.removeFromSuperview()
                }
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            })
        }
    }
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return XOConfig.TransitionAnimation.pureDuration
    }
}
